home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************
- * fbham.c: FBM Release 1.0 25-Feb-90 Michael Mauldin
- *
- * Copyright (C) 1989,1990 by C. Harald Koch & Michael Mauldin.
- * Permission is granted to use this file in whole or in part for
- * any purpose, educational, recreational or commercial, provided
- * that this copyright notice is retained unchanged. This software
- * is available to all free of charge by anonymous FTP and in the
- * UUNET archives.
- *
- * fbham.c: Write a 24bit RGB file as an Amiga HAM IFF file
- *
- * USAGE
- * % fbham < image1 > image2
- *
- * EDITLOG
- * LastEditDate = Mon Jun 25 00:56:54 1990 - Michael Mauldin
- * LastFileName = /usr2/mlm/src/misc/fbm/fbham.c
- *
- * HISTORY
- * 13-Jul-89 Michael Mauldin (mlm) at Carnegie Mellon University
- * Beta release (version 0.95) mlm@cs.cmu.edu
- *
- * 20-Apr-89 C. Harald Koch (chk) at DCIEM Toronto.
- * Created. chk@ben.dciem.dnd.ca
- *
- *================================================================
- * based on ray2.c from DBW_Render, Copyright 1987 David B. Wecker
- *
- * From: chk@dretor.dciem.dnd.ca (C. Harald Koch)
- * Subject: fbham.c - convert a 24bit FBM file to an IFF file using HAM mode
- * To: Michael.Mauldin@nl.cs.cmu.edu (Michael Maudlin)
- * Date: Mon, 1 May 89 17:21:16 EDT
- * X-Mailer: ELM [version 2.2 PL0]
- *
- * This is the source to my program to convert from a 24bit FBM file to Amiga
- * HAM mode. It is based on Dave Wecker's RAY2 program, which converts the
- * output of his raytracer to HAM mode. His code uses the Amiga graphics
- * library to plot the pixels in a framebuffer for encoding; this version will
- * run standalone.
- *
- * There may be bugs, although it works well for me here. It is probably not in
- * the format you want for FBM source; Please go ahead and modify it. There is
- * probably room for some command line options for various things (such as the
- * verbose output, run length encoding, etc) which I haven't needed and so
- * haven't added.
- *
- * I will send you the IRIS programs when I get them. Enjoy!
- * -chk
- *****************************************************************/
-
- #include <stdio.h>
- #include <math.h>
- #include "fbm.h"
-
- # define USAGE "Usage: fbham < image > image"
-
- #ifndef lint
- static char *fbmid =
- "$FBM fbham.c <1.0> 25-Jun-90 (C) 1989 by C. Harald Koch, source \
- code available free from MLM@CS.CMU.EDU and from UUNET archives$";
-
- #endif
-
- #define DEPTH 6 /* max depth of Amiga HAM image */
-
- #define BMHDsize 20L /* chunk sizes */
- #define CMAPsize 96L
- #define CAMGsize 4L
- #define BODYsize ((long)(16000L))
- #define FORMsize (BODYsize+CAMGsize+CMAPsize+BMHDsize+36L)
-
- unsigned char *planes[DEPTH]; /* bitplane pointers */
-
- static int comp = 1; /* compress image? */
- static int depth = DEPTH; /* depth of image being created */
- static int colors = 16; /* number of colors in color lookup table */
- static int colorstats[4096][2]; /* color usage statistics */
- static int row_size; /* number of bytes in IFF row of data */
-
- /* temp variables for writing IFF file */
- char str[40];
- long lng, pos1, pos2;
- short wrd;
- unsigned char byt;
- unsigned char *dest, destbuf[BUFSIZ];
-
-
- main(argc, argv)
- char *argv[];
- {
- FBM input;
-
- /* Clear the memory pointers so alloc_fbm won't be confused */
- input.cm = input.bm = (unsigned char *) NULL;
-
- /* Read the image and rotate it */
- if (!read_bitmap(&input, argc > 0 ? argv[1] : (char *) NULL))
- {
- exit(1);
- }
-
- /* slight sanity checks. could be better. */
- if (input.hdr.physbits != 8 || input.hdr.planes != 3) {
- fprintf(stderr, "input file must be 24 bit RGB data\n");
- exit(1);
- }
-
- /* convert to HAM */
- if (!fbm2ham(&input, stdout)) {
- exit(1);
- }
-
- exit(0);
- }
-
-
- /************************ run length encoding from Amiga RKM *****************/
- #define DUMP 0 /* list of different bytes */
- #define RUN 1 /* single run of bytes */
- #define MinRun 3 /* shortest allowed run */
- #define MaxRun 128 /* longest run (length is signed char) */
- #define MaxDat 128 /* longest block of unencoded data */
- #define GetByte() (*source++)
- #define PutByte(c) { *dest++ = (c); ++PutSize; }
- #define OutDump(nn) dest = PutDump(dest,nn);
- #define OutRun(nn,cc) dest = PutRun(dest,nn,cc);
-
- int PutSize;
- char buf[256];
-
- unsigned char *
- PutDump(dest, nn)
- unsigned char *dest;
- int nn;
- {
- int i;
-
- PutByte(nn - 1);
- for (i = 0; i < nn; i++)
- PutByte(buf[i]);
- return (dest);
- }
-
- unsigned char *
- PutRun(dest, nn, cc)
- unsigned char *dest;
- int nn, cc;
- {
- PutByte(-(nn - 1));
- PutByte(cc);
- return (dest);
- }
-
- /* PackRow - pack a row of data using Amiga IFF RLE */
- int
- PackRow(pSource, pDest, RowSize)
- unsigned char **pSource, **pDest;
- int RowSize;
- {
- unsigned char *source, *dest;
- char c, lastc = '\000';
- int mode = DUMP, nbuf = 0, /* number of chars in buf */
- rstart = 0; /* buf index current run starts */
-
- source = *pSource;
- dest = *pDest;
-
- PutSize = 0;
- buf[0] = lastc = c = GetByte();
- nbuf = 1;
- RowSize--;
-
- for (; RowSize; --RowSize) {
- buf[nbuf++] = c = GetByte();
-
- switch (mode) {
- case DUMP:
- if (nbuf > MaxDat) {
- OutDump(nbuf - 1);
- buf[0] = c;
- nbuf = 1;
- rstart = 0;
- break;
- }
- if (c == lastc) {
- if (nbuf - rstart >= MinRun) {
- if (rstart > 0)
- OutDump(rstart);
- mode = RUN;
- }
- else if (rstart == 0)
- mode = RUN;
- }
- else
- rstart = nbuf - 1;
- break;
-
- case RUN:
- if ((c != lastc) || (nbuf - rstart > MaxRun)) {
- OutRun((nbuf - 1) - rstart, lastc);
- buf[0] = c;
- nbuf = 1;
- rstart = 0;
- mode = DUMP;
- }
- break;
- }
- lastc = c;
- }
- switch (mode) {
- case DUMP:
- OutDump(nbuf);
- break;
- case RUN:
- OutRun(nbuf - rstart, lastc);
- break;
- }
-
- *pSource = source;
- *pDest = dest;
-
- return (PutSize);
- }
- /******************* end of RKM RL encoding routines **********************/
-
-
- /* build_histogram - count frequency of 12bit colors in an FBM image */
- build_histogram(image)
- FBM *image;
- {
- int i, j, t0, t1, gap, val, used;
- unsigned char *rp, *gp, *bp;
- int diff;
-
- /* initialize color statistics. */
- for (i = 0; i < 4096; i++) {
- colorstats[i][0] = i;
- colorstats[i][1] = 0;
- }
-
- /* obtain pointers to the beginning of each plane (Red, Green, Blue) */
- rp = image->bm;
- gp = rp + image->hdr.plnlen;
- bp = gp + image->hdr.plnlen;
-
- /* count the number of occurences of each color in the image */
- for (i = 0 ; i < image->hdr.plnlen ; i++) {
- val = ((*rp++ & 0xf0) << 4) + (*gp++ & 0xf0) + ((*bp++ & 0xf0) >> 4);
- val %= 4096;
- if (colorstats[val][1] < 32767) {
- colorstats[val][1]++;
- }
- }
-
- /* sort the color stats in order of decreasing usage */
- for (gap = 2048; gap > 0; gap /= 2) {
- for (i = gap; i < 4096; i++) {
- for (j = i - gap; j >= 0 &&
- colorstats[j][1] < colorstats[j + gap][1]; j -= gap) {
- t0 = colorstats[j][0];
- t1 = colorstats[j][1];
- colorstats[j][0] = colorstats[j + gap][0];
- colorstats[j][1] = colorstats[j + gap][1];
- colorstats[j + gap][0] = t0;
- colorstats[j + gap][1] = t1;
- }
- }
- }
-
- /* count the number of colors actually used in the image */
- for (used = 0; used < 4096 && colorstats[used][1] > 0; used++);
- fprintf(stderr, "Used %d colors out of a possible 4096\n", used);
- }
-
- /*-
- * plot_pixel - plot a color in an Amiga style bitmap (one plane per bit)
- *
- * Description:
- * A somewhat optimized routine to set/reset one bit in each plane
- * at the specified (x,y) coordinates. plane i gets the bit in
- * position i of color. a replacement for the Amiga WritePixel call.
- -*/
- plot_pixel(planes, x, y, color)
- unsigned char *planes[];
- int x, y;
- register int color;
- {
- register int bit;
- register unsigned char shifted_bit = 1 << (7-(x%8));
- register int array_offset = y * row_size + x/8;
- register int i;
-
- for (i = 0; color && i < depth; i++) {
- bit = color & 1;
- color >>= 1;
-
- if (bit) *(planes[i] + array_offset) |= shifted_bit;
- }
- }
-
-
- /*-
- * fbm2ham - write an FBM image in HAM IFF format on the given file pointer.
- *
- -*/
- fbm2ham(image, ofil)
- FBM *image;
- FILE *ofil;
- {
- int i, j, k, gap, t0, t1, prgb, crgb, cpix, ppix, maxdis, used;
- int c1, c2, diff;
- unsigned char *rp, *gp, *bp;
-
- fprintf(stderr, "Building a color histogram:\n");
- build_histogram(image);
-
- /* convert the image to an Amiga HAM bitplane based image */
- cpix = 0;
- crgb = colorstats[0][1];
-
- rp = image->bm;
- gp = rp + image->hdr.plnlen;
- bp = gp + image->hdr.plnlen;
-
- row_size = ((image->hdr.cols + 15) / 16) * 2;
- diff = image->hdr.rowlen - image->hdr.cols;
-
- for (i = 0; i < depth ; i++) {
- planes[i] = (unsigned char *)malloc(row_size * image->hdr.rows);
- }
-
- for (i = 0; i < image->hdr.rows; i ++) {
- /* verbose output because this program is slow even on a Sun */
- if (i%50 == 0) { fprintf(stderr, "...%d", i); fflush(stderr); }
-
- /* step through current row, converting FBM pixel to nearest equivalent
- * HAM pixel */
- for (j = 0; j < image->hdr.cols; j++) {
- prgb = crgb;
- crgb = ((*rp++ & 0xf0) << 4) + (*gp++ & 0xf0) + ((*bp++ & 0xf0) >> 4);
- crgb %= 4096;
- ppix = cpix;
-
- /* start of scan line is ALWAYS an absolute color */
- if (j == 0)
- cpix = getcolor(ppix, &crgb, -1);
- else
- cpix = getcolor(ppix, &crgb, prgb);
-
- /* plot the computed pixel */
- plot_pixel(planes, j, i, cpix);
- }
- rp += diff;
- gp += diff;
- bp += diff;
- }
- fprintf(stderr, "\n");
-
- /* Now we write the planes[] array we just created in ILBM format */
- sprintf(str, "FORM");
- fwrite(str, 1, 4, ofil);
- pos1 = ftell(ofil);
- lng = FORMsize;
- fwrite(&lng, 4, 1, ofil);
- sprintf(str, "ILBM");
- fwrite(str, 1, 4, ofil);
-
- sprintf(str, "BMHD");
- fwrite(str, 1, 4, ofil);
- lng = BMHDsize;
- fwrite(&lng, 4, 1, ofil);
- wrd = image->hdr.cols;
- fwrite(&wrd, 2, 1, ofil); /* width */
- wrd = image->hdr.rows;
- fwrite(&wrd, 2, 1, ofil); /* height */
- wrd = 0;
- fwrite(&wrd, 2, 1, ofil); /* top */
- wrd = 0;
- fwrite(&wrd, 2, 1, ofil); /* left */
- byt = depth;
- fwrite(&byt, 1, 1, ofil); /* Depth */
- byt = 0;
- fwrite(&byt, 1, 1, ofil); /* mask */
- byt = comp;
- fwrite(&byt, 1, 1, ofil); /* compress */
- byt = 0;
- fwrite(&byt, 1, 1, ofil); /* pad */
- wrd = 0;
- fwrite(&wrd, 2, 1, ofil); /* transparency */
- byt = 10;
- fwrite(&byt, 1, 1, ofil); /* aspect x */
- byt = 11;
- fwrite(&byt, 1, 1, ofil); /* aspect y */
- wrd = image->hdr.cols;
- fwrite(&wrd, 2, 1, ofil); /* page width */
- wrd = image->hdr.rows;
- fwrite(&wrd, 2, 1, ofil); /* page height */
-
- /* CAMG chunk for displaying files on the Amiga. should include at least
- * the HAM flag; I also add lace if the picture is large enough. Perhaps
- * this should be an option. -CHK */
- sprintf(str, "CAMG");
- fwrite(str, 1, 4, ofil);
- lng = CAMGsize;
- fwrite(&lng, 4, 1, ofil);
- if (image->hdr.rows > 200)
- lng = 0x804L; /* HAM | LACE */
- else
- lng = 0x800L; /* HAM */
- fwrite(&lng, 4, 1, ofil);
-
- /* write out the color lookup table */
- sprintf(str, "CMAP");
- fwrite(str, 1, 4, ofil);
- lng = CMAPsize;
- fwrite(&lng, 4, 1, ofil);
- for (i = 0; i < 32; i++) {
- str[0] = (colorstats[i][0] >> 4) & 0xF0;
- str[1] = (colorstats[i][0]) & 0xF0;
- str[2] = (colorstats[i][0] << 4) & 0xF0;
- fwrite(str, 1, 3, ofil);
- }
-
- sprintf(str, "BODY");
- fwrite(str, 1, 4, ofil);
- pos2 = ftell(ofil);
- lng = BODYsize;
- fwrite(&lng, 4, 1, ofil);
-
- lng = 0L;
-
- for (i = 0; i < image->hdr.rows; i ++) {
- for (j = 0; j < depth; j++) {
- if (comp) {
- dest = destbuf;
- wrd = PackRow(&planes[j], &dest, row_size);
- lng += (long) wrd;
- fwrite(destbuf, 1, wrd, ofil);
- }
- else {
- fwrite(planes[j], 1, row_size, ofil);
- planes[j] += row_size;
- }
- }
- }
-
- /* make BODY a multiple of 4 bytes in length */
- byt = 0;
- if (comp)
- while (lng % 4) {
- fwrite(&byt, 1, 1, ofil); /* pad */
- lng++;
- }
-
- if (comp) {
- fseek(ofil, (long) pos2, 0);
- fwrite(&lng, 4, 1, ofil);
- lng -= BODYsize;
- lng += FORMsize;
- fseek(ofil, (long) pos1, 0);
- fwrite(&lng, 4, 1, ofil);
- }
-
- return 1;
- }
-
- /* get the next encoding for a pixel, based on the previous pixel and the
- * current 12 bit RGB value */
-
- #define BPP 4 /* Bits per pixel */
- #define MAXGRAY (1 << BPP)
- #define ABS(x) ((x) < 0 ? -(x) : (x))
-
- /*-
- * first, check to see if pixel is the same color. if so, return
- * check if only one of R, G, B have changed. if so, return new pixel.
- * find closest entry in colormap. if exact match, return it.
- * modify one of R, G, B (whichever difference is largest)
- * compare previous calculation to best match in color table. return
- * whichever is a better match.
- -*/
- getcolor(ppix, crgb, prgb)
- int ppix, *crgb, prgb;
- {
- int i, j, val, cr, cg, cb, pr, pg, pb, nr, ng, nb, best, dist, nrgb;
-
- /* if same color, then do a NOOP (same as previous pixel) */
- if (*crgb == prgb)
- return (ppix);
-
- /* set up for comparisons */
- cb = *crgb & (MAXGRAY - 1);
- cg = (*crgb >> BPP) & (MAXGRAY - 1);
- cr = (*crgb >> (BPP * 2)) & (MAXGRAY - 1);
-
- pb = prgb & (MAXGRAY - 1);
- pg = (prgb >> BPP) & (MAXGRAY - 1);
- pr = (prgb >> (BPP * 2)) & (MAXGRAY - 1);
-
- /* see if only one plane changed, if so, use HAM encoding */
- if (prgb != -1) {
- if (pr == cr && pg == cg)
- return (cb + 0x10);
- if (pr == cr && pb == cb)
- return (cg + 0x30);
- if (pg == cg && pb == cb)
- return (cr + 0x20);
- }
-
- /* else look for an exact match in the color table (or minimal distance) */
- for (i = 0; i < colors; i++) {
- if (colorstats[i][0] == *crgb)
- return (i);
- if (i == 0) {
- best = 0;
- dist = distance(colorstats[i][0], *crgb);
- }
- else if ((j = distance(colorstats[i][0], *crgb)) < dist) {
- best = i;
- dist = j;
- }
- }
-
- /* do a forced absolute */
- if (prgb == -1) {
- *crgb = colorstats[best][0];
- return (best);
- }
-
- /* find which color is off the most from previous */
- i = 0;
- val = ABS(cr - pr);
- if (ABS(cg - pg) > val) {
- i = 1;
- val = ABS(cg - pg);
- }
- if (ABS(cb - pb) > val) {
- i = 2;
- val = ABS(cb - pb);
- }
-
- nr = pr;
- ng = pg;
- nb = pb;
- switch (i) {
- case 0:
- nr = cr;
- val = nr + 0x20;
- break;
- case 1:
- ng = cg;
- val = ng + 0x30;
- break;
- case 2:
- nb = cb;
- val = nb + 0x10;
- break;
- }
- nrgb = (nr << (2 * BPP)) + (ng << BPP) + nb;
-
- /* now pick the best */
- if (distance(nrgb, *crgb) < dist) {
-
- /* do a best relative */
- *crgb = nrgb;
- return (val);
- }
-
- /* do a best absolute */
- *crgb = colorstats[best][0];
- return (best);
- }
-
- /* calculate distance between two colors in 3D space */
- distance(argb, brgb)
- {
- int b, g, r;
-
- /* set up for comparisons */
- b = argb & (MAXGRAY - 1);
- g = (argb >> BPP) & (MAXGRAY - 1);
- r = (argb >> (BPP * 2)) & (MAXGRAY - 1);
-
- b -= brgb & (MAXGRAY - 1);
- g -= (brgb >> BPP) & (MAXGRAY - 1);
- r -= (brgb >> (BPP * 2)) & (MAXGRAY - 1);
-
- return (r * r + g * g + b * b);
- }
-